/*
 * Javlov - a Java toolkit for reinforcement learning with multi-agent support.
 * 
 * Copyright (c) 2009 Matthijs Snel
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package net.javlov;

import java.util.Arrays;

/**
 * Class that provides a basic implementation of the ContinuousAction interface. Subclasses only
 * need to implement {@link #getMaxRange()}, {@link #getMinRange()}, and {@link #execute(Agent)}.
 * 
 * @author Matthijs Snel
 *
 */
public abstract class AbstractContinuousAction extends AbstractPrimitiveAction implements ContinuousAction {

	private static final long serialVersionUID = 6695367929452993940L;
	
	/**
	 * The values the action is currently holding.
	 */
	protected double[] values;
	
	/**
	 * Dimensionality of the action. Subclasses need to set this.
	 */
	protected int dim;

	
	protected AbstractContinuousAction() {}
	
	protected AbstractContinuousAction(double[] values) {
		this.values = values;
	}
	
	/**
	 * @return the dimensionality of the action (how many values it requires)
	 */
	@Override
	public int getDimensionality() {
		return dim;
	}
	
	/**
	 * Returns a reference to the values the action is currently holding.
	 * @return the values the action is currently holding.
	 */
	public double[] getValues() {
		return values;
	}
	
	/**
	 * Sets the dimensionality of the action (how many values it requires).
	 * @param dimensionality the dimensionality.
	 */
	protected void setDimensionality(int dimensionality) {
		dim = dimensionality;
	}
	
	/**
	 * Sets the action values to the specified values.
	 * 
	 * @param vals the values to set the action to.
	 * @throws IllegalArgumentException if the length of the specified array of values does not
	 * match the dimensionality of the action.
	 */
	@Override
	public void setValues(double[] vals) {
		if ( vals.length != dim )
			throw new IllegalArgumentException("Required " + dim + " values but received:" + vals.length);
		values = vals;
	}
	
	/**
	 * Compares this object against the specified object. The result is true if and only if the
	 * argument is not null and is a {@code AbstractContinuousAction} of the same class as the
	 * class of this object, of which the value array
	 * has the same length as the array of this object, and all values in the arrays are equal
	 * according to the method specified in {@link Double#equals(Object)}.
	 * 
	 * Note that it usually doesn't make much sense to compare double values for equality, so unless
	 * the double values in the array actually represent integers, or only a discrete set of
	 * values (e.g. either 0, 0.25, 0.5, 0.75 or 1), this method will usually return false.
	 * 
	 * @param the object to compare with
	 * @return {@code true} if the objects are the same; {@code false} otherwise.
	 */
	@Override
	public boolean equals( Object o ) {
		if ( o == null || !(o instanceof AbstractContinuousAction) )
			return false;
		if ( !getClass().equals(o.getClass()) )
			return false;
		AbstractContinuousAction act = (AbstractContinuousAction) o;
		return Arrays.equals(values, act.values);
	}
	
	/**
	 * Returns the hash code for this continuous action, according to the methods specified in 
	 * {@link List} and {@link Double}.
	 */
	@Override
	public int hashCode() {
		return Arrays.hashCode(values);
	}
	
	@Override
	public String toString() {
		String s = getClass().toString() + ": [";
		for ( int i = 0; i < values.length; i++ ) {
			s += values[i] + " ";
		}
		s += "]";
		return s;
	}
}
